use std::{fs, io::Read, path::Path};

use crate::{TextSignFormat, process::get_reader};
use anyhow::Result;
use base64::{Engine, prelude::BASE64_URL_SAFE_NO_PAD};

trait TextSign {
    fn sign(&self, reader: &mut dyn Read) -> Result<Vec<u8>>;
}
trait TextVerify {
    fn verify(&self, reader: &mut dyn Read, sig: &[u8]) -> Result<bool>;
}

struct Blake3 {
    key: [u8; 32],
}
// struct Ed25519Signer {
//     key: [u8; 32],
// }
// struct Ed25519Verifier {
//     key: [u8; 32],
// }

impl Blake3 {
    fn new(key: [u8; 32]) -> Self {
        Self { key }
    }

    fn try_new(key: &[u8]) -> Result<Self> {
        let key = &key[..32];
        let key = key.try_into()?;
        Ok(Blake3::new(key))
    }

    fn load(path: impl AsRef<Path>) -> Result<Self> {
        let key = fs::read(path)?;
        Self::try_new(&key)
    }
}

impl TextSign for Blake3 {
    fn sign(&self, reader: &mut dyn Read) -> Result<Vec<u8>> {
        let mut buf = Vec::new();
        reader.read_to_end(&mut buf)?;
        Ok(blake3::keyed_hash(&self.key, &buf).as_bytes().to_vec())
    }
}

impl TextVerify for Blake3 {
    fn verify(&self, reader: &mut dyn Read, sig: &[u8]) -> Result<bool> {
        let mut buf = Vec::new();
        reader.read_to_end(&mut buf)?;
        let hash = blake3::keyed_hash(&self.key, &buf);
        let hash = hash.as_bytes();
        Ok(hash == sig)
    }
}

pub fn process_text_sign(input: &str, key: &str, format: TextSignFormat) -> Result<String> {
    let mut reader = get_reader(input)?;
    // let mut buf = Vec::new();
    // reader.read_to_end(&mut buf)?;
    let signed = match format {
        TextSignFormat::Blake3 => {
            let signer = Blake3::load(key)?;
            signer.sign(&mut reader)?
        }
        TextSignFormat::Ed25519 => todo!(),
    };
    let signed = BASE64_URL_SAFE_NO_PAD.encode(&signed);
    Ok(signed)
}

pub fn process_text_verify(
    input: &str,
    key: &str,
    sig: &str,
    format: TextSignFormat,
) -> Result<bool> {
    let mut reader = get_reader(input)?;
    let verifier = match format {
        TextSignFormat::Blake3 => {
            let verify = Blake3::load(key)?;
            verify
        }
        TextSignFormat::Ed25519 => todo!(),
    };
    let sig = get_content(sig)?;
    verifier.verify(&mut reader, &sig)
}

pub fn process_generate_key(format: TextSignFormat) -> Result<Vec<Vec<u8>>> {
    match format {
        TextSignFormat::Blake3 => {
            // let key = [0u8; 32];
            let key = crate::process_genpass(32, false, false, false, false)?;
            let key: Vec<u8> = key.into_bytes();
            Ok(vec![key])
        }
        TextSignFormat::Ed25519 => {
            todo!()
        }
    }
}

pub fn get_content(input: &str) -> Result<Vec<u8>> {
    let mut reader = get_reader(input)?;
    let mut buf = Vec::new();
    reader.read_to_end(&mut buf)?;
    Ok(buf)
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_blake3_sign_verify() -> Result<()> {
        let signer = Blake3::load("./fixtures/blake3.txt")?;
        let verifier = Blake3::load("./fixtures/blake3.txt")?;

        let data = b"hello world";
        let sig = signer.sign(&mut &data[..]).unwrap();
        assert!(verifier.verify(&mut &data[..], &sig).unwrap());
        Ok(())
    }
}
